package cn.ecasoft.service.impl;


import cn.ecasoft.basic.datatable.DataTable;
import cn.ecasoft.configproperties.EcaSqlsConfig;
import cn.ecasoft.constant.AllowProperties;
import cn.ecasoft.constant.ColType;
import cn.ecasoft.pojo.dto.CollectorDto;
import cn.ecasoft.pojo.vo.CollectDataVo;
import cn.ecasoft.pojo.vo.CollectorVo;
import cn.ecasoft.redis.RedisService;
import cn.ecasoft.pojo.ClientSocket;
import cn.ecasoft.service.EnergyConsumptionService;
import cn.ecasoft.util.EncryptUtil;
import cn.ecasoft.utils.DBhelper;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Author: LiangChun
 * @Description: https
 * @DateTime: 2022/10/25
 **/
@Slf4j
@Service
public class EnergyConsumptionServiceImpl implements EnergyConsumptionService {
    private ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    @Autowired
    private DBhelper dbHelper;
    @Autowired
    private EcaSqlsConfig ecaSqlsConfig;
    @Autowired
    private RedisService redisService;
    @Autowired
    private AllowProperties allowProperties;

    private Integer randomSeconds = 3 * 60 * 60 + ((int) (Math.random() * 30)) * 60 + (int) (Math.random() * 30);
/*
    @Override
    public Result<Object> putCollerctorData(MultipartFile file, HttpServletRequest request) throws IOException {
        if (file.isEmpty()) {
            return new Result<>().error("文件不能为空");
        }
        String s = new String(file.getBytes(), "utf-8").replace("\n", "").replace("\t", "").replace("\r", "");
        List<MeterOriginDTO> meterOriginDTOS;
        try {
            *//*JsonReader reader = new JsonReader(inputStreamReader);
            Gson gson = new GsonBuilder().create();
            reader.beginArray();
            while (reader.hasNext()) {
                // Read data into object model
                MeterOriginDTO data = gson.fromJson(reader, MeterOriginDTO.class);
            }
            reader.close();*//*
            meterOriginDTOS = JSON.parseArray(s, MeterOriginDTO.class);
            String validResult = BeanValidateUtil.validate(meterOriginDTOS);
            if (StringUtils.isNotEmpty(validResult) || validResult != null) {
                throw new Exception("缺失" + validResult);
            }
        } catch (Exception e) {
            return new Result<>().error(e.getMessage());
        }
        if (meterOriginDTOS.size() == 0 || ObjectUtils.isEmpty(meterOriginDTOS)) {
            return new Result<>().error("无数据");
        }
        cachedThreadPool.execute(() -> {
            saveCollectorData(meterOriginDTOS);
        });
        return new Result<>().ok();
    }

    public void saveCollectorData(List<MeterOriginDTO> meterOriginDTOS) {
//        List<HashMap> newMeterDto = JSON.parseArray(JSON.toJSONString(meterOriginDTOS, SerializerFeature.WriteNullStringAsEmpty).replace("\"\"",null), HashMap.class);
        try {
            dbHelper.StartTrans(() -> {
                String sqlKey = UUID.randomUUID().toString();
                try {
                    //执行一次insert
                    StringBuffer sql = new StringBuffer("insert into LJ_Monitor (pkid) values ");
                    Integer pkid = null;
                    for (int i = 0; i < meterOriginDTOS.size(); i++) {
                        pkid = dbHelper.GetPkValue("", "LJ_Monitor", "pkid");
                        sql.append("(" + pkid + ")" + (i == meterOriginDTOS.size() - 1 ? "" : ","));
                    }
                    ecaSqlsConfig.getMap().put(sqlKey, sql);
                    dbHelper.QueryInt("", sqlKey, null);
                    //执行n次update(总字段太多)
                    for (int i = 0; i < meterOriginDTOS.size(); i++) {
                        String newSql = "update LJ_Monitor set " + JSON.toJSONString(meterOriginDTOS.get(i)).replace(":\"", "='").replace("\",", "',").replace("{\"", "").replace("\"}", "'").replace("\"", "").replace(",,", ",")
                                + " where pkid = " + (pkid - (meterOriginDTOS.size() - 1 - i));
                        ecaSqlsConfig.getMap().put(sqlKey, newSql);
                        dbHelper.QueryInt("", sqlKey, null);
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e.getMessage());
                } finally {
                    ecaSqlsConfig.getMap().remove(sqlKey);
                }
            });
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }*/

    /**
     * 不包含鉴权在内
     *
     * @param
     * @return : java.lang.String
     */
    public String handleCollectorRequest(ClientSocket clientSocket, String inputMessage) {
        List<CollectorDto> collectorRs = new ArrayList<>();
        try {
            List<CollectorDto> collectorDtos = Optional.ofNullable(resolveMessage(inputMessage)).orElse(new ArrayList<>());
            if (collectorDtos.size() == 0) return "解析加密文本失败";
            //以下是解析成功之后的步骤
            for (CollectorDto shareDto : collectorDtos) {
                //1. 解析之后，所有的解析成功的采集器都有对应的唯一guid,即使编号完全相同
                String randomChars = UUID.randomUUID().toString().replace("-", "");
                String mCode = Optional.ofNullable(shareDto.getM_Code()).orElse("");
                String type = Optional.ofNullable(shareDto.getM_Type()).orElse("");
                Long time = System.currentTimeMillis() / 1000;
                if (StringUtils.isEmpty(mCode) || StringUtils.isEmpty(type)) continue;

                CollectorDto collectorR = new CollectorDto().setM_Code(shareDto.getM_Code());

                switch (type.toLowerCase()) {
                    case ColType.IN_AUTH:
                        if(collectorR.getM_Code().length()!=19){
                            collectorR.setM_Type(ColType.OUT_AUTH_FAIL);
                            collectorR.setM_Value("长度为19位");
                            break;
                        }
                        collectorR.setM_Type(ColType.OUT_AUTH_ACK).setM_Value(randomChars);
                        //2.1 鉴权部分：将自身加密后的字符串和row_guid均保存在redis hash的过期时间只是hashkey的过期时间，不是里面单个key的过期时间
                        redisService.hset(mCode, "encrypt", EncryptUtil.getMD5_HEX(randomChars + allowProperties.getAes_key()), randomSeconds);
                        redisService.hset(mCode, "row_guid", randomChars, randomSeconds);
                        break;
                    case ColType.IN_AUTH_ACK:
                        collectorR.setM_Type(ColType.OUT_AUTH_FAIL);
                        //2.2 对比encrypt,如通过鉴权则存储到lj_meter_collector_building
                        if (StringUtils.isNotEmpty(shareDto.getM_Value()) && redisService.hget(mCode, "encrypt").equals(shareDto.getM_Value())) {
                            redisService.hset(mCode, "isAuthorized", "1");
                            collectorR.setM_Type(ColType.OUT_AUTH_PASS);
                            saveMessage(new CollectorVo(redisService.hget(mCode, "row_guid").toString(), mCode,
                                    mCode.substring(0, 6), mCode.substring(6, 8), mCode.substring(8, 11), mCode.substring(0, 11),
                                    mCode.substring(11, 13), mCode.substring(13, 16), mCode.substring(0, 16),
                                    mCode.substring(16)));
                        }
                        break;
                    // 以下部分都要查看是否通过鉴权
                    case ColType.IN_NOTIFY:
                        if (checkifAuthorized(shareDto, randomSeconds)) {
                            collectorR.setM_Type(ColType.OUT_NOTIFY_ACK);
                            collectorR.setM_Value(String.valueOf(time));
                            clientSocket.setCurrentLong(time);
                        } else {
                            collectorR.setM_Type(ColType.OUT_AUTH_FAIL);
                        }
                        break;
                    case ColType.IN_REPORT:
                        if (checkifAuthorized(shareDto, randomSeconds) || mCode.length() > 19) {
                            clientSocket.setCurrentLong(time);
                            boolean b = saveMessage(new CollectDataVo(mCode, mCode.substring(0, 19),
                                    mCode.substring(19), mCode.substring(19, 20), mCode.substring(20, 22),
                                    mCode.substring(22, 23), mCode.substring(23, 24), mCode.substring(24, 25),
                                    shareDto.getM_Up_Time(), shareDto.getM_Value()));
                            collectorR.setM_Type(ColType.OUT_REPORT_ACK);
                            if (StringUtils.isEmpty(shareDto.getM_Value()) || !b) {
                                collectorR.setM_Type(ColType.OUT_REPORT_FAIL);
                            }
                        } else {
                            collectorR.setM_Type(ColType.OUT_AUTH_FAIL);
                        }
                        break;
                    case ColType.IN_CONTINUOUS:
                        if (checkifAuthorized(shareDto, randomSeconds) || mCode.length() > 19) {
                            clientSocket.setCurrentLong(time);
                            boolean b = saveMessage(new CollectDataVo(mCode, mCode.substring(0, 19), mCode.substring(19, 25),
                                    mCode.substring(19, 20), mCode.substring(20, 22), mCode.substring(22, 23), mCode.substring(23, 24),
                                    mCode.substring(24, 25), shareDto.getM_Up_Time(), shareDto.getM_Value()));
                            collectorR.setM_Type(ColType.OUT_CONTINUOUS_ACK);
                            if (StringUtils.isEmpty(shareDto.getM_Value()) || !b) {
                                collectorR.setM_Type(ColType.OUT_CONTINUOUS_FAIL);
                            }
                        } else {
                            collectorR.setM_Type(ColType.OUT_AUTH_FAIL);
                        }
                        break;
                    default:
                        break;
                }
                collectorRs.add(collectorR);
            }
            return encrypted(collectorRs);
        } catch (Exception e) {
            e.printStackTrace();
            collectorRs.add(new CollectorDto());
            return encrypted(collectorRs);
        }
    }


    public static void main(String[] args) {
        EnergyConsumptionServiceImpl energyConsumptionService = new EnergyConsumptionServiceImpl();
        String temp = "55aa55aa94000000000000007a0871f1553056e894d2b9075280074b1ea21477744251a477ceb65a70579fe09e0b80d97f0bb707199c178697414846e767f77fecdcf1eccb1885f7de6c0ee0f99a8519830c5cf924797e1ca306ce80c1d6fe61545a5b17dcb5294ed9a42713eef24587dfa78ce6a4466fc410e092d7ba7e15851a5e659fb9de429319f98207d192ecf1b6a93ae2a2da3b03ed4556e768681616";
        List<CollectorDto> collectorDtos = energyConsumptionService.resolveMessage1(temp);

//        System.out.println(Integer.toHexString(170));
    }

    private String encrypted(List<CollectorDto> collectorRs) {
        try {
            String temp = EncryptUtil.encrypt_HEX_AES_CBC(JSON.toJSONString(collectorRs), allowProperties.getKey(), allowProperties.getIv());
            StringBuilder intext = new StringBuilder(Integer.toHexString(temp.length() / 2 + 4));
            while (intext.length() < 8) {
                intext.append("0");
            }
            String result = "55AA55AA" + intext + "00000000" + temp + "68681616\r";
            return new String(EncryptUtil.hex2bytes(result), StandardCharsets.ISO_8859_1);
        } catch (Exception e) {
            log.error(e.getMessage());
            return null;
        }
    }

    /**
     * 解密，所有的文本进来均要解密
     *
     * @param inputMessage
     * @return : java.util.List<cn.ecasoft.pojo.dto.CollectorDto>
     */
    private List<CollectorDto> resolveMessage(String inputMessage) {
        try {
            inputMessage = inputMessage.replace(" ", "");
            inputMessage = inputMessage.substring(24, inputMessage.indexOf("68681616"));
            String dataJsonstring = EncryptUtil.decrpty_HEX_AES_CBC(inputMessage, allowProperties.getKey(), allowProperties.getIv());
            if (dataJsonstring.indexOf("]") == -1) return null;
            System.out.println(dataJsonstring.indexOf("["));
            dataJsonstring = dataJsonstring.substring(0, dataJsonstring.indexOf("]") + 1);
            log.info("CollectorDto : {}", dataJsonstring);
            List<CollectorDto> collectorDtos = JSON.parseArray(dataJsonstring, CollectorDto.class);
            return collectorDtos;
        } catch (Exception e) {
            //鉴权失败
            log.error(e.getMessage());
            return null;
        }
    }


    private List<CollectorDto> resolveMessage1(String inputMessage) {
        inputMessage = inputMessage.replace(" ", "");
        inputMessage = inputMessage.substring(24, inputMessage.indexOf("68681616"));
        try {
            String dataJsonstring = EncryptUtil.decrpty_HEX_AES_CBC(inputMessage, ColType.key, ColType.iv);
            if (dataJsonstring.indexOf("]") == -1) return null;
            System.out.println(dataJsonstring.indexOf("["));
            dataJsonstring = dataJsonstring.substring(0, dataJsonstring.indexOf("]") + 1);
            List<CollectorDto> collectorDtos = JSON.parseArray(dataJsonstring, CollectorDto.class);
            return collectorDtos;
        } catch (Exception e) {
            //鉴权失败
            log.error(e.getMessage());
            return null;
        }
    }

    /**
     * 保存采集器基本信息
     *
     * @param input
     * @return : boolean
     */
    public boolean saveMessage(CollectorVo input) {
        String sqk = UUID.randomUUID().toString();
        String sqlString = "INSERT ignore INTO lj_meter_collector_building(row_guid,m_Code,m_Area_Number,m_Year,m_Project_Serial,m_Project_Code,m_Collectors_Code,m_Acquisition_Code,m_Project_Acquisition,m_Building_Code) VALUES ( @row_guid, @m_Code, @m_Area_Number, @m_Year, @m_Project_Serial, @m_Project_Code, @m_Collectors_Code, @m_Acquisition_Code, @m_Project_Acquisition, @m_Building_Code)";
        ecaSqlsConfig.getMap().put(sqk, sqlString);
        try {
            System.out.println(JSON.toJSONString(input));
            int i = dbHelper.QueryInt("", sqk, input);
        } catch (Exception e) {
            return false;
        } finally {
            ecaSqlsConfig.getMap().remove(sqk);
        }
        return true;
    }

    /**
     * 保存采集到的数据
     *
     * @param input
     * @return : boolean
     */
    public boolean saveMessage(CollectDataVo input) {
        String sqk = UUID.randomUUID().toString();
        String sqlString = "INSERT INTO lj_meter_collect_data( M_Code, M_Co_Building_Code, M_Energy_Code, M_Energy_Type_Code, M_Consumption_Code, M_Primary_Code, M_Secondary_Code, M_Up_Time, M_Value) VALUES ( @m_Code, @m_Co_Building_Code, @m_Energy_Code, @m_Energy_Type_Code, @m_Consumption_Code, @m_Primary_Code, @m_Secondary_Code, @m_Up_Time, @m_Value)";
        ecaSqlsConfig.getMap().put(sqk, sqlString);
        try {
            int i = dbHelper.QueryInt("", sqk, input);
        } catch (Exception e) {
            return false;
        } finally {
            ecaSqlsConfig.getMap().remove(sqk);
        }
        return true;
    }

    public boolean selectCollector(String M_Code) {
        String sqk = UUID.randomUUID().toString();
        String sqlString = "select row_guid from lj_meter_collector_building where M_Code=@M_Code ";
        ecaSqlsConfig.getMap().put(sqk, sqlString);
        try {
            DataTable dataRows = dbHelper.QueryDataTable("", sqk, new HashMap<String, Object>() {{
                put("M_Code", M_Code);
            }});
            return dataRows.HasRow();
        } catch (Exception e) {
            log.error(e.getMessage());
            return false;
        } finally {
            ecaSqlsConfig.getMap().remove(sqk);
        }
    }

    /**
     * 保存数据部分 查看是否通过鉴权
     *
     * @param collectorDto
     * @return : boolean
     */
    public boolean checkifAuthorized(CollectorDto collectorDto, Integer randonSeconds) {

        if ("1".equals(Optional.ofNullable(redisService.hget(collectorDto.getM_Code().substring(0, 19), "isAuthorized")).orElse(""))
                || selectCollector(collectorDto.getM_Code())) {
            redisService.hset(collectorDto.getM_Code().substring(0, 19), "isAuthorized", "1", randonSeconds);
            return true;
        }
        return false;
    }

}
